using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows.Forms;

using System.Runtime.InteropServices;

namespace Qreed.Windows.Forms
{
    public partial class FolderTreeView : TreeView
    {
        //private ShellIconReader _shellIconReader;

        /// <summary>
        /// Gets or sets the selected path.
        /// </summary>
        /// <value>The selected path.</value>
        public string SelectedPath
        {
            get 
            {
                if (SelectedNode != null && SelectedNode.GetType().IsAssignableFrom(typeof(ShellTreeNode)))
                {
                    ShellTreeNode node = (ShellTreeNode)SelectedNode;
                    return node.Path;
                }

                return null;
            }
            set 
            {
                value = MakeSafePath(value);

                if (string.IsNullOrEmpty(value))
                    return;

                ShellTreeNode node = LocateNode(value);
                
                if (node != null)
                {
                    SelectedNode = node;
                    node.EnsureVisible();
                }
            }
        }

        /// <summary>
        /// Gets the selected shell tree node.
        /// </summary>
        /// <value>The selected shell tree node.</value>
        public ShellTreeNode SelectedShellTreeNode
        {
            get { return (ShellTreeNode)SelectedNode; }
        }

        /// <summary>
        /// Gets the selected shell PIDL.
        /// </summary>
        /// <value>The selected shell PIDL.</value>
        public ShellPIDL SelectedShellPIDL
        {
            get
            {
                if (SelectedShellTreeNode != null)
                    return SelectedShellTreeNode.ShellPIDL;

                return null;
            }
        }

        /// <summary>
        /// Gets the selected shell folder.
        /// </summary>
        /// <value>The selected shell folder.</value>
        public ShellFolder SelectedShellFolder
        {
            get
            {
                if (SelectedShellPIDL != null)
                    return SelectedShellPIDL.ShellFolder;

                return null;
            }
        }

        private ShellFolder.ItemType _includeFlags = ShellFolder.ItemType.Folder;
        /// <summary>
        /// Gets or sets a value indicating whether to include only folders.
        /// </summary>
        /// <value><c>true</c> if to include only folders; otherwise, <c>false</c>.</value>
        public ShellFolder.ItemType IncludeFlags
        {
            get { return this._includeFlags; }
            set { this._includeFlags = value; }
        }

        private string MakeSafePath(string path)
        {
            if (path == null)
                return null;

            path = path.Trim();

            // Trim any leading slashes unless it's a drive letter

            int i = path.IndexOf(":");

            if (i == path.Length - 1)
            {
                path += '\\'; // Path ends in a ':', append a slash
            }
            else
            {
                path = path.TrimEnd("\\".ToCharArray());
            }

            if (path == string.Empty)
                return null;

            return path;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FolderTreeView"/> class.
        /// </summary>
        public FolderTreeView()
        {
            InitializeComponent();
            InitializeImageList();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FolderTreeView"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        public FolderTreeView(IContainer container)
        {
            container.Add(this);

            InitializeComponent();
            InitializeImageList();
        }

        private void InitializeImageList()
        {
            SystemImageList.AssignToTreeView(this);
        }

        /// <summary>
        /// Populates the tree.
        /// </summary>
        public void PopulateTree()
        {
            BeforeExpand += new TreeViewCancelEventHandler(FolderTreeView_BeforeExpand);

            ShellTreeNode desktopNode = new ShellTreeNode(IncludeFlags);

            Nodes.Add(desktopNode);
            desktopNode.Expand();
        }

        /// <summary>
        /// Refreshes the selected node.
        /// </summary>
        public void RefreshSelectedNode()
        {
            if (SelectedNode != null && SelectedNode is ShellTreeNode)
                ((ShellTreeNode)SelectedNode).Refresh();
        }

        void FolderTreeView_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            try
            {
                BeginUpdate();
                Cursor.Current = Cursors.WaitCursor;

                if(e.Node.GetType().IsAssignableFrom(typeof(ShellTreeNode)))
                {
                    ShellTreeNode shellNode = (ShellTreeNode)e.Node;
                    shellNode.PopulateSubNodes();
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
                EndUpdate();
            }
        }

        internal ShellTreeNode LocateNode(string path)
        {
            ShellPIDL pidlTarget = ShellPIDL.FromPath(path);
            
            ShellTreeNode currentNode = (ShellTreeNode)Nodes[0];
            ShellPIDL[] ancestors = pidlTarget.GetAncestors(true);

            if (ancestors == null)
                return null;

            for (int i = ancestors.Length - 2; i != -1; i--)
            {
                ShellPIDL pidl = ancestors[i];

                if (!currentNode.IsExpanded)
                    currentNode.Expand();

                if (!currentNode.Nodes.ContainsKey(pidl.DisplayName))
                {
                    // Current node does not contain the next required node
                    // Try a refresh
                    
                    currentNode.Refresh();

                    if (!currentNode.IsExpanded)
                        currentNode.Expand();

                    if (!currentNode.Nodes.ContainsKey(pidl.DisplayName))
                        break; // Current node still doesn't contains the required node .. return the best match
                }
                    
                currentNode = (ShellTreeNode)currentNode.Nodes[pidl.DisplayName];
            }

            return currentNode;
        }

    }
}
